mod env;
mod error;
#[cfg(test)]
mod test;

pub use env::*;

use crate::consts::*;
use crate::object::*;
use crate::compiler::*;

use error::EvalError;

use std::cell::RefCell;
use std::cmp::Ordering;
use std::error::Error;
use std::rc::Rc;


macro_rules! make_err {
    ( $($e:expr),* ) => { Err(Box::new(EvalError::new(format!( $($e),* )))) };
}

pub fn eval(program: &str, env: &mut Rc<RefCell<Env>>) -> Result<Rc<Object>, Box<dyn Error>> {
    let obj = compile(program)?;
    eval_obj(obj, env)
}

fn eval_obj(obj: Rc<Object>, env: &mut Rc<RefCell<Env>>) -> Result<Rc<Object>, Box<dyn Error>> {
    match obj.as_ref() {
        Object::CodeList(objs) => eval_code_list(objs, env),
        Object::Symbol(symbol) => eval_symbol(symbol, env),
        Object::Void | Object::Lambda(_, _, _) => Ok(VOID_OBJ.clone()),
        _ => Ok(obj),
    }
}

fn eval_code_list(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    match objs.first() {
        Some(head) => match eval_obj(head.clone(), env)?.as_ref() {
            Object::Operator(op) => eval_operator(op, &objs[1..], env),
            Object::Keyword(keyword) => eval_keyword(&keyword, &objs[1..], env),
            Object::Lambda(params, body, lambda_env) => {
                eval_lambda_invoke(params, body, lambda_env.clone(), &objs[1..], env)
            }
            Object::Symbol(symbol_name) => {
                if let Object::Lambda(params, body, lambda_env) =
                    eval_symbol(symbol_name, env)?.as_ref()
                {
                    eval_lambda_invoke(params, body, lambda_env.clone(), &objs[1..], env)
                } else {
                    make_err!("Only Lambda object can be invoked")
                }
            }
            _ => make_err!("Invalid object: {:?}", head),
        },
        None => Ok(VOID_OBJ.clone()),
    }
}

fn eval_symbol(
    symbol_name: &str,
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    // Const value
    match symbol_name {
        TRUE_VAL => return Ok(Rc::new(Object::Bool(true))),
        FALSE_VAL => return Ok(Rc::new(Object::Bool(false))),
        VOID_VAL => return Ok(VOID_OBJ.clone()),
        _ => match env.borrow_mut().get(symbol_name) {
            Some(object) => Ok(object),
            None => make_err!("Unbound symbol: {}", symbol_name),
        },
    }
}

fn eval_operator(
    op: &str,
    args: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    let mut objs = Vec::new();
    for arg in args {
        objs.push(eval_obj(arg.clone(), env)?);
    }

    match op {
        ADD_OP | SUB_OP | MUL_OP | DIV_OP | REM_OP | LES_OP | GRT_OP => {
            if objs.iter().any(|obj| !obj.is_number()) {
                return make_err!("Invalid types for {} operator", op);
            }

            if objs.iter().any(|obj| obj.is_float()) {
                let mut numbers = objs
                    .into_iter()
                    .map(|obj| match obj.as_ref() {
                        Object::Integer(n) => *n as f64,
                        Object::Float(n) => *n,
                        _ => unreachable!(),
                    })
                    .collect::<Vec<_>>();

                match op {
                    ADD_OP => Ok(Rc::new(Object::Float(numbers.into_iter().sum()))),
                    MUL_OP => Ok(Rc::new(Object::Float(numbers.into_iter().product()))),
                    SUB_OP | DIV_OP | REM_OP | LES_OP | GRT_OP => {
                        if numbers.len() != 2 {
                            make_err!("Invalid number of arguments for {} operator", op)
                        } else {
                            let right = numbers.pop().unwrap();
                            let left = numbers.pop().unwrap();
                            match op {
                                SUB_OP => Ok(Rc::new(Object::Float(left - right))),
                                DIV_OP => Ok(Rc::new(Object::Float(left / right))),
                                REM_OP => Ok(Rc::new(Object::Float(left % right))),
                                LES_OP => Ok(Rc::new(Object::Bool(left < right))),
                                GRT_OP => Ok(Rc::new(Object::Bool(left > right))),
                                _ => unreachable!(),
                            }
                        }
                    }
                    _ => unreachable!(),
                }
            } else {
                let mut numbers = objs
                    .into_iter()
                    .map(|obj| {
                        if let Object::Integer(n) = obj.as_ref() {
                            return *n;
                        }
                        unreachable!()
                    })
                    .collect::<Vec<_>>();

                match op {
                    ADD_OP => Ok(Rc::new(Object::Integer(numbers.into_iter().sum()))),
                    MUL_OP => Ok(Rc::new(Object::Integer(numbers.into_iter().product()))),
                    SUB_OP | DIV_OP | REM_OP | LES_OP | GRT_OP => {
                        if numbers.len() != 2 {
                            make_err!("Invalid number of arguments for {} operator", op)
                        } else {
                            let right = numbers.pop().unwrap();
                            let left = numbers.pop().unwrap();
                            match op {
                                SUB_OP => Ok(Rc::new(Object::Integer(left - right))),
                                DIV_OP => Ok(Rc::new(Object::Integer(left / right))),
                                REM_OP => Ok(Rc::new(Object::Integer(left % right))),
                                LES_OP => Ok(Rc::new(Object::Bool(left < right))),
                                GRT_OP => Ok(Rc::new(Object::Bool(left > right))),
                                _ => unreachable!(),
                            }
                        }
                    }
                    _ => unreachable!(),
                }
            }
        }
        "and" | "or" => {
            // Must all Bool
            if objs.iter().any(|obj| !obj.is_bool()) {
                return make_err!("Invalid types for {} operator", op);
            }

            let mut bools = objs.into_iter().map(|obj| {
                if let Object::Bool(b) = obj.as_ref() {
                    return *b;
                }
                unreachable!()
            });

            if op == "and" {
                Ok(Rc::new(Object::Bool(bools.all(|b| b))))
            } else {
                Ok(Rc::new(Object::Bool(bools.any(|b| b))))
            }
        }
        "=" | "!=" => {
            if objs.len() != 2 {
                make_err!("Invalid number of arguments for {} operator", op)
            } else {
                let right_obj = objs[1].as_ref();
                let left_obj = objs[0].as_ref();
                if op == "=" {
                    match (&left_obj, &right_obj) {
                        (Object::Integer(l), Object::Integer(r)) => {
                            Ok(Rc::new(Object::Bool(l == r)))
                        }
                        (Object::String(l), Object::String(r)) => Ok(Rc::new(Object::Bool(l == r))),
                        _ => make_err!(
                            "Invalid types for = operator {:?} {:?}",
                            left_obj,
                            right_obj
                        ),
                    }
                } else {
                    match (&left_obj, &right_obj) {
                        (Object::Integer(l), Object::Integer(r)) => {
                            Ok(Rc::new(Object::Bool(l != r)))
                        }
                        (Object::Float(l), Object::Float(r)) => Ok(Rc::new(Object::Bool(l != r))),
                        (Object::Integer(l), Object::Float(r)) => {
                            Ok(Rc::new(Object::Bool(*l as f64 != *r)))
                        }
                        (Object::Float(l), Object::Integer(r)) => {
                            Ok(Rc::new(Object::Bool(*l != (*r) as f64)))
                        }
                        (Object::String(l), Object::String(r)) => {
                            Ok(Rc::new(Object::Bool(l.cmp(&r) != Ordering::Equal)))
                        }
                        _ => make_err!(
                            "Invalid types for != operator {:?} {:?}",
                            left_obj,
                            right_obj
                        ),
                    }
                }
            }
        }
        _ => make_err!("Invalid infix operator: {}", op),
    }
}

fn eval_keyword(
    keyword: &str,
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    match keyword {
        IF => eval_if_keyword(objs, env),
        DEFINE => eval_define_keyword(objs, env),
        LAMBDA => eval_lambda_keyword(objs, env),
        BEGIN => eval_begin_keyword(objs, env),
        LET => eval_let_keyword(objs, env),

        LIST => eval_list_keyword(objs, env),
        CONS => eval_cons_keyword(objs, env),
        RANGE => eval_range_keyword(objs, env),
        MAP => eval_map_keyword(objs, env),
        FILTER => eval_filter_keyword(objs, env),
        FOLDL => eval_foldl_keyword(objs, env),
        FOLDR => eval_foldr_keyword(objs, env),
        REDUCE => eval_reduce_keyword(objs, env),
        CAR => eval_car_keyword(objs, env),
        CDR => eval_cdr_keyword(objs, env),
        LENGTH => eval_length_keyword(objs, env),
        EMPTY => eval_empty_keyword(objs, env),

        _ => make_err!("Unknown keyword: {}", keyword),
    }
}

fn eval_let_keyword(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    // (let ((x 1) (y 2)) (+ x y))
    if objs.len() < 2 {
        return make_err!("'let' needs at least 2 argument");
    }

    // Must be a code list
    let binding_list = match objs[0].as_ref() {
        Object::CodeList(l) => l,
        _ => return make_err!("'let' second argument must be a CodeList"),
    };

    // Binding each object 
    let ew_env = Rc::new(RefCell::new(Env::extend(env.clone())));
    let mut binding_env = Rc::new(RefCell::new(Env::new()));
    binding_env.borrow_mut().update(ew_env);

    for binding in binding_list {
        // (name value)
        let binding = match binding.as_ref() {
            Object::CodeList(l) => l,
            _ => return make_err!("'let' second argument must be a CodeList"),
        };

        if binding.len() != 2 {
            return make_err!("Each binding should a '(name value)' structure");
        }

        let name = match binding[0].as_ref() {
            Object::Symbol(name) => name,
            _ => return make_err!("Each binding should a '(name value)' structure"),
        };

        let value = eval_obj(binding[1].clone(), env)?;

        binding_env.borrow_mut().set(name.clone(), value);
    }

    let mut result = VOID_OBJ.clone();
    for exe_obj in &objs[1..] {
        result = eval_obj(exe_obj.clone(), &mut binding_env)?;
    }

    Ok(result)
}

fn eval_begin_keyword(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    let mut new_env = Rc::new(RefCell::new(Env::extend(env.clone())));
    let mut result = VOID_OBJ.clone();

    for obj in objs {
        result = eval_obj(obj.clone(), &mut new_env)?;
    }

    Ok(result)
}

fn eval_empty_keyword(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    if objs.len() != 1 {
        return make_err!("'length' needs 1 argument");
    }

    let obj = eval_obj(objs[0].clone(), env)?;
    if let Object::List(list) = obj.as_ref() {
        Ok(Rc::new(Object::Bool(list.len() == 0)))
    } else {
        make_err!("'length' expect a list, but got: '{:?}'", obj)
    }
}

fn eval_length_keyword(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    if objs.len() != 1 {
        return make_err!("'length' needs 1 argument");
    }

    let obj = eval_obj(objs[0].clone(), env)?;
    if let Object::List(list) = obj.as_ref() {
        Ok(Rc::new(Object::Integer(list.len() as i64)))
    } else {
        make_err!("'length' expect a list, but got: '{:?}'", obj)
    }
}

fn eval_car_keyword(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    if objs.len() != 1 {
        return make_err!("'car' needs 1 argument");
    }

    let obj = eval_obj(objs[0].clone(), env)?;
    if let Object::List(list) = obj.as_ref() {
        if list.is_empty() {
            make_err!("car: empty list")
        } else {
            Ok(list[0].clone())
        }
    } else {
        make_err!("'car' expect a list, but got: '{:?}'", obj)
    }
}

fn eval_cdr_keyword(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    if objs.len() != 1 {
        return make_err!("'cdr' needs 1 argument");
    }

    let obj = eval_obj(objs[0].clone(), env)?;
    if let Object::List(list) = obj.as_ref() {
        if list.is_empty() {
            make_err!("cdr: empty list")
        } else {
            Ok(Rc::new(Object::List(
                list.into_iter().skip(1).map(|obj| obj.clone()).collect(),
            )))
        }
    } else {
        make_err!("'cdr' expect a list, but got: '{:?}'", obj)
    }
}

fn eval_range_keyword(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    if objs.len() != 2 && objs.len() != 3 {
        return make_err!("'range' needs 2 or 3 arguments");
    }

    macro_rules! must_integer {
        ( $o:expr, $param:expr ) => {{
            match eval_obj($o.clone(), env)?.as_ref() {
                Object::Integer(n) => *n,
                _ => {
                    return make_err!(
                        "For 'range' keyword, '{}' argument expects an integer",
                        $param
                    )
                }
            }
        }};
    }

    // Get Start & End
    let start = must_integer!(objs[0], "start");
    let end = must_integer!(objs[1], "end");
    let step = if objs.len() == 3 {
        must_integer!(objs[2], "step")
    } else {
        1
    };

    use std::cmp::Ordering;
    Ok(Rc::new(Object::List(
        match (start.cmp(&end), step.cmp(&0)) {
            (Ordering::Less, Ordering::Greater) => (start..end)
                .into_iter()
                .step_by(step as usize)
                .map(|n| Rc::new(Object::Integer(n)))
                .collect(),
            (Ordering::Greater, Ordering::Less) => (end..start)
                .into_iter()
                .step_by((-step) as usize)
                .map(|n| Rc::new(Object::Integer(n)))
                .collect::<Vec<_>>()
                .into_iter()
                .rev()
                .collect(),
            _ => vec![],
        },
    )))
}

fn eval_reduce_keyword(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    // (reduce f l)
    if objs.len() != 2 {
        return make_err!("'reduce' needs 2 arguments");
    }

    let lambda = eval_obj(objs[0].clone(), env)?;
    let list = eval_obj(objs[1].clone(), env)?;

    if let Object::Lambda(params, body, lambda_env) = lambda.as_ref() {
        if let Object::List(list) = list.as_ref() {
            if !list.is_empty() {
                let mut init = list[0].clone();
                for obj in list.iter().skip(1) {
                    init = eval_lambda_invoke(
                        params,
                        body,
                        lambda_env.clone(),
                        &vec![init.clone(), obj.clone()],
                        env,
                    )?
                }
                Ok(init)
            } else {
                make_err!("reduce: empty list")
            }
        } else {
            make_err!("'reduce' second argument must be a list")
        }
    } else {
        make_err!("'reduce' first argument must be a lambda")
    }
}

fn eval_foldl_keyword(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    // (foldl f init l)
    if objs.len() != 3 {
        return make_err!("'foldl' needs 3 arguments");
    }

    let lambda = eval_obj(objs[0].clone(), env)?;
    let mut init = eval_obj(objs[1].clone(), env)?;
    let list = eval_obj(objs[2].clone(), env)?;

    if let Object::Lambda(params, body, lambda_env) = lambda.as_ref() {
        if let Object::List(list) = list.as_ref() {
            for obj in list {
                init = eval_lambda_invoke(
                    params,
                    body,
                    lambda_env.clone(),
                    &vec![init.clone(), obj.clone()],
                    env,
                )?
            }
            Ok(init)
        } else {
            make_err!("'foldl' second argument must be a list")
        }
    } else {
        make_err!("'foldl' first argument must be a lambda")
    }
}

fn eval_foldr_keyword(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    // (foldr f init l)
    if objs.len() != 3 {
        return make_err!("'foldr' needs 3 arguments");
    }

    let lambda = eval_obj(objs[0].clone(), env)?;
    let mut init = eval_obj(objs[1].clone(), env)?;
    let list = eval_obj(objs[2].clone(), env)?;

    if let Object::Lambda(params, body, lambda_env) = lambda.as_ref() {
        if let Object::List(list) = list.as_ref() {
            for obj in list.into_iter().rev() {
                init = eval_lambda_invoke(
                    params,
                    body,
                    lambda_env.clone(),
                    &vec![obj.clone(), init.clone()],
                    env,
                )?
            }
            Ok(init)
        } else {
            make_err!("'foldr' second argument must be a list")
        }
    } else {
        make_err!("'foldr' first argument must be a lambda")
    }
}

fn eval_map_keyword(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    // (map f l)
    if objs.len() != 2 {
        return make_err!("'map' needs two arguments");
    }

    let lambda = eval_obj(objs[0].clone(), env)?;
    let list = eval_obj(objs[1].clone(), env)?;

    if let Object::Lambda(params, body, lambda_env) = lambda.as_ref() {
        if let Object::List(list) = list.as_ref() {
            let mut new_list = Vec::new();
            for obj in list {
                new_list.push(eval_lambda_invoke(
                    params,
                    body,
                    lambda_env.clone(),
                    &vec![obj.clone()],
                    env,
                )?);
            }
            Ok(Rc::new(Object::List(new_list)))
        } else {
            make_err!("'map' second argument must be a list")
        }
    } else {
        make_err!("'map' first argument must be a lambda")
    }
}

fn eval_filter_keyword(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    // (filter f l)
    if objs.len() != 2 {
        return make_err!("'filter' needs two arguments");
    }

    let lambda = eval_obj(objs[0].clone(), env)?;
    let list = eval_obj(objs[1].clone(), env)?;

    if let Object::Lambda(params, body, lambda_env) = lambda.as_ref() {
        if let Object::List(list) = list.as_ref() {
            let mut new_list = Vec::new();
            for obj in list {
                let cond =
                    eval_lambda_invoke(params, body, lambda_env.clone(), &vec![obj.clone()], env)?;
                match cond.as_ref() {
                    Object::Bool(b) => {
                        if *b {
                            new_list.push(obj.clone());
                        }
                    }
                    _ => {
                        return make_err!(
                            "'filter' second argument must be a lambda which return bool"
                        )
                    }
                }
            }
            Ok(Rc::new(Object::List(new_list)))
        } else {
            make_err!("'filter' second argument must be a list")
        }
    } else {
        make_err!("'filter' first argument must be a lambda")
    }
}

fn eval_cons_keyword(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    // (cons 1 (list 2 3))
    if objs.len() != 2 {
        return make_err!("Invalid number of arguments for 'cons' statement");
    }

    let head = eval_obj(objs[0].clone(), env)?;
    let list = eval_obj(objs[1].clone(), env)?;

    if let Object::List(list) = list.as_ref() {
        let mut new_list = Vec::new();
        new_list.push(head);
        for obj in list {
            new_list.push(obj.clone());
        }
        Ok(Rc::new(Object::List(new_list)))
    } else {
        make_err!("'cons' second arguments must be a Object::List")
    }
}

fn eval_lambda_keyword(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    // (lambda (x) (+ x x))
    create_lambda(objs, env)
}

fn eval_list_keyword(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    let mut list = Vec::new();
    for obj in objs {
        list.push(eval_obj(obj.clone(), env)?);
    }
    Ok(Rc::new(Object::List(list)))
}

fn eval_define_keyword(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    match objs.len() {
        2 => eval_value_define(objs, env),
        3 => eval_lambda_define(objs, env),
        _ => make_err!("Invalid number of arguments for define"),
    }
}

fn eval_value_define(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    // Try define value
    let symbol_name = objs[0].clone();
    let obj = objs[1].clone();
    let value = eval_obj(obj, env)?;

    if let Object::Symbol(symbol_name) = symbol_name.as_ref() {
        env.borrow_mut().set(symbol_name.clone(), value);
        Ok(VOID_OBJ.clone())
    } else {
        make_err!("Invalid symbol for define")
    }
}

fn eval_lambda_define(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    let symbol_name = objs[0].clone();

    if let Object::Symbol(symbol_name) = symbol_name.as_ref() {
        let lambda = create_lambda(&objs[1..], env)?;
        env.borrow_mut().set(symbol_name.clone(), lambda);
        Ok(VOID_OBJ.clone())
    } else {
        make_err!("Invalid symbol for define")
    }
}

fn create_lambda(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    if objs.len() != 2 {
        return make_err!("Create a Lambda need two object as params and body");
    }

    let params = objs[0].clone();
    let body = objs[1].clone();

    // Build a Object::Lambda
    if let Object::CodeList(params) = params.as_ref() {
        // All params should be symbol
        if params.iter().all(|param| param.is_symbol()) {
            // Take string from params
            let params = params
                .into_iter()
                .map(|param| {
                    if let Object::Symbol(s) = param.as_ref() {
                        return s.clone();
                    }
                    unreachable!()
                })
                .collect::<Vec<_>>();
            Ok(Rc::new(Object::Lambda(params, body, env.clone())))
        } else {
            make_err!("Lambda params should be a list of symbol")
        }
    } else {
        make_err!("Lambda params should be a list")
    }
}

fn eval_if_keyword(
    objs: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    if objs.len() != 3 {
        return make_err!("Invalid number of arguments for if statement");
    }

    let cond_obj = objs[0].clone();
    let true_obj = objs[1].clone();
    let false_obj = objs[2].clone();

    if let Object::Bool(b) = eval_obj(cond_obj, env)?.as_ref() {
        if *b {
            eval_obj(true_obj, env)
        } else {
            eval_obj(false_obj, env)
        }
    } else {
        make_err!("Condition must be a boolean")
    }
}

fn eval_lambda_invoke(
    params: &[ObjString],
    body: &Rc<Object>,
    lambda_env: Rc<RefCell<Env>>,
    args: &[Rc<Object>],
    env: &mut Rc<RefCell<Env>>,
) -> Result<Rc<Object>, Box<dyn Error>> {
    if args.len() != params.len() {
        make_err!(
            "Params size '{}' not equal to aguments size '{}'",
            params.len(),
            args.len()
        )
    } else {
        let mut new_env = Rc::new(RefCell::new(Env::extend(lambda_env.clone())));
        for (param, arg) in params.into_iter().zip(args.into_iter()) {
            let value = eval_obj(arg.clone(), env)?;
            new_env.borrow_mut().set(param.to_objstring(), value);
        }
        eval_obj(body.clone(), &mut new_env)
    }
}

